package com.wxt.asmplugins;


import com.android.build.api.transform.Context;
import com.android.build.api.transform.DirectoryInput;
import com.android.build.api.transform.Format;
import com.android.build.api.transform.JarInput;
import com.android.build.api.transform.QualifiedContent;
import com.android.build.api.transform.Transform;
import com.android.build.api.transform.TransformException;
import com.android.build.api.transform.TransformInput;
import com.android.build.api.transform.TransformOutputProvider;
import com.android.build.gradle.internal.pipeline.TransformManager;

import org.gradle.api.Project;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;

import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * @author wuxiaotao on 2018/6/7.
 */
public class MyTestTransForm extends Transform {

    Project mProject;

    private static ArrayList<String> INJECT_METHOD = new ArrayList<>();

    static {
        INJECT_METHOD.add("onClick(Landroid/view/View;)V");
    }

    public MyTestTransForm(Project project) {
        System.out.print("=========测试transform==========");
        this.mProject = project;
    }


    //transform的名称
    //transformClassesWithMyClassTransformForDebug 运行时的名字
    //transformClassesWith + getName() + For + Debug或Release
    @Override
    public String getName() {
        return "MyTransformJava";
    }

    @Override
    public Set<QualifiedContent.ContentType> getInputTypes() {
        return TransformManager.CONTENT_CLASS;
    }

    @Override
    public Set<QualifiedContent.Scope> getScopes() {
        return TransformManager.SCOPE_FULL_PROJECT;
    }

    @Override
    public boolean isIncremental() {
        //是否支持增量编译
        return false;
    }

    @Override
    public void transform(Context context, Collection<TransformInput> inputs, Collection<TransformInput> referencedInputs,
                          TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException {
        log("============transfor start===========");
        for (TransformInput input : inputs) {
            //文件操作
            Collection<DirectoryInput> directoryInputs = input.getDirectoryInputs();
            for (DirectoryInput directoryInput : directoryInputs) {
                //输出路径
                File fileDest = outputProvider.getContentLocation(directoryInput.getName(), directoryInput.getContentTypes(), directoryInput.getScopes(), Format.DIRECTORY);
                fileDest.delete();
                fileDest.mkdirs();
//                //直接拷贝文件夹
                FileUtil.copyDir(directoryInput.getFile(), fileDest);
                //拷贝后的文件夹 修改.class文件
                File dir = fileDest;
                LinkedList<File> list = new LinkedList();

                File file[] = dir.listFiles();
                for (int i = 0; i < file.length; i++) {
                    if (file[i].isDirectory()) {
                        list.add(file[i]);
                    } else {
                        inject(file[i]);
                    }
                }
                File tmp;
                while (!list.isEmpty()) {
                    tmp = list.removeFirst();
                    if (tmp.isDirectory()) {
                        file = tmp.listFiles();
                        if (file == null)
                            continue;
                        for (int i = 0; i < file.length; i++) {
                            if (file[i].isDirectory()) {
                                list.add(file[i]);
                            } else {
                                inject(file[i]);
                            }
                        }
                    } else {
                        inject(tmp);
                    }
                }
            }
//            //jar操作
            Collection<JarInput> jarInputs = input.getJarInputs();
            for (JarInput jarInput : jarInputs) {
                //输出路径
                File jarDest = outputProvider.getContentLocation(jarInput.getName(), jarInput.getContentTypes(), jarInput.getScopes(), Format.JAR);
                //直接拷贝文件jar包
                FileUtil.copyFile(jarInput.getFile(), jarDest);
                log("intput jar name=  " + jarInput.getName());
                log("intput jar path=  " + jarInput.getFile().getPath());


                log("output jar path=  " + jarDest.getName());
                //jar包中代码注入
//                ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(jarDest));
                ZipFile zipFile = new ZipFile(jarInput.getFile());
                try {
                    Enumeration<? extends ZipEntry> enumeration = zipFile.entries();
                    while (enumeration.hasMoreElements()) {
                        ZipEntry entry = enumeration.nextElement();
                        //zip中文件名
                        log("zip file name ======" + entry.getName());
                        InputStream stream = zipFile.getInputStream(entry);

                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    closeQuietly(zipFile);
                }

            }
        }
        log("============transfor end===========");
    }


    static void closeQuietly(Closeable closeable) {
        try {
            if (closeable != null) {
                closeable.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean needInject(String className) {
        //可以添加白名单 忽略
        return (className.endsWith(".class")
                && !className.contains("R$")
                && !className.contains("R.class")
                && !className.contains("BuildConfig.class")
                && !className.contains("Test"));

    }

    private void inject(File file) {
        String className = file.getName();
        if (needInject(className)) {

            log("inject find =======" + className);
            ClassNode node = new ClassNode();
            ClassReader classReader = null;
            try {
                FileInputStream inputStream = new FileInputStream(file);
                classReader = new ClassReader(inputStream);
            } catch (Exception e) {
                log("===================" + e.toString());
                return;
            }
            classReader.accept(node, ClassReader.EXPAND_FRAMES);
            //文件中存在onClickMethod
            for (String injectMethod : INJECT_METHOD) {
                log("inject Method " + injectMethod);
                List<MethodNode> methods = node.methods;
                for (MethodNode methodNode : methods) {
                    if ((node.access & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE) {
                        continue;
                    }
                    if (injectMethod.contains(methodNode.name + methodNode.desc)) {
                        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                        MyTestInjector injector = new MyTestInjector(classWriter);
//                        OnClickInjector injector = new OnClickInjector(classWriter, mProject, "");
                        classReader.accept(injector, 0);
                        byte[] bytes = classWriter.toByteArray();
                        log("=========开始注入==========" + bytes.length);
                        InputStream inputStream = new ByteArrayInputStream(bytes);
                        try {
                            FileOutputStream outputStream = new FileOutputStream(file);
                            FileUtil.copyStream(inputStream, outputStream);
                            outputStream.close();
                            log("=========注入结束==========" + file.length());
                        } catch (Exception e) {
                            log("=========注入异常==========" + e.getMessage());
                        }
                    }
                }
            }

        }
    }


//    private void injectByStream(InputStream inputStream, OutputStream outputStream){
//        ClassNode node = new ClassNode();
//        ClassReader classReader = null;
//        try {
//            classReader = new ClassReader(inputStream);
//        } catch (Exception e) {
//            log("===================" + e.toString());
//            return;
//        }
//        classReader.accept(node, ClassReader.EXPAND_FRAMES);
//        //文件中存在onClickMethod
//        for (String injectMethod : INJECT_METHOD) {
//            log("inject Method " + injectMethod);
//            List<MethodNode> methods = node.methods;
//            for (MethodNode methodNode : methods) {
//                if ((node.access & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE) {
//                    continue;
//                }
//                if (injectMethod.contains(methodNode.name + methodNode.desc)) {
//                    ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
//                    MyTestInjector injector = new MyTestInjector(classWriter);
////                        OnClickInjector injector = new OnClickInjector(classWriter, mProject, "");
//                    classReader.accept(injector, 0);
//                    byte[] bytes = classWriter.toByteArray();
//                    log("=========开始注入==========" + bytes.length);
//                    InputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
//                    try {
//                        FileUtil.copyStream(byteArrayInputStream, outputStream);
//                        outputStream.close();
//                        log("=========注入结束==========");
//                    } catch (Exception e) {
//                        log("=========注入异常==========" + e.getMessage());
//                    }
//                }
//            }
//        }
//
//    }


    private void log(String log) {
        System.out.println("===================" + log);
    }
}
